﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Twinkle.Data;
using Krile.Kernel.WindowsEx;
using Krile.Kernel;
using Twinkle.Tweak;
using System.Threading;

namespace Twinkle.Common
{
    public static class StreamManager
    {
        static StreamManager()
        {
            var dar = new Action(() => dequeAndRegister());
            dar.BeginInvoke((iar) => dar.EndInvoke(iar), null);
        }

        static bool shutdown = false;
        static Dictionary<Account, UserStreams> streamsDictionary = new Dictionary<Account, UserStreams>();

        public static bool UserStreamConnected(Account acc)
        {
            return streamsDictionary.ContainsKey(acc);
        }

        public static void ConnectUserStreams(Account acc)
        {
            if (!acc.UseUserStreams || streamsDictionary.ContainsKey(acc))
            {
                return;
            }
            var us = new UserStreams();
            streamsDictionary.Add(acc, us);
            var act = new Action(() => Receiver(us, acc));
            act.BeginInvoke((iar) => act.EndInvoke(iar), null);
        }

        public static void ReconnectUserStreams(Account acc)
        {
            if (streamsDictionary.ContainsKey(acc))
            {
                bool n;
                streamsDictionary[acc].EndStreaming();
                var desc = Common.AutoReceiver.GetDescriptor(acc, out n);
                while (desc.UserStreamsConnected || UserStreamConnected(acc))
                {
                    Thread.Sleep(100);
                }
            }
            ConnectUserStreams(acc);
        }

        public static void EndStreamingAll()
        {
            shutdown = true;
            if (streamsDictionary.Count > 0)
            {
                foreach (var s in streamsDictionary.Values)
                {
                    s.EndStreaming();
                }
            }
        }

        private static void Receiver(UserStreams streams, Account acc)
        {
            bool n = false;
            do
            {
                var desc = Common.AutoReceiver.GetDescriptor(acc, out n);
                try
                {
                    var strm = streams.GetTimelineStream(acc);
                    if (strm != null)
                    {
                        desc.UserStreamsConnected = true;
                        foreach (var elem in strm)
                        {
                            if (!acc.UseUserStreams) // User streams 使用終了
                                break;
                            switch (elem.Kind)
                            {
                                case TChirpElement.ElementKind.Status:
                                case TChirpElement.ElementKind.Retweet:
                                    TreatRecv(elem.Status);
                                    TreatRecv(elem.Status);
                                    break;
                            }
                        }
                        // 接続が切れました
                        desc.UserStreamsConnected = false;
                    }
                }
                catch (Exception e)
                {
                    Bridge.DebugReporter.AddReport(e);
                }
                finally
                {
                    // 接続に失敗したか、または切れている
                    desc.UserStreamsConnected = false;
                }
                // 接続終了
                if (!shutdown && acc.UseUserStreams)
                {
                    shutdown = MessageBoxEx.Show(
                        String.Format(Lang.Msg.UserStreamDisconnected, acc.UserId),
                        Lang.Msg.UserStreamDisconnected_Title,
                        MessageBoxEx.MessageBoxExButtons.YesNo, MessageBoxEx.MessageBoxExIcon.Error) != MessageBoxEx.DialogResultEx.Yes;
                }
            } while (!shutdown);

            if (streamsDictionary.ContainsKey(acc))
                streamsDictionary.Remove(acc);
        }

        static ManualResetEvent dequeWaiter = new ManualResetEvent(false);
        private static object sqLock = new object();
        static Queue<TStatus> statusQueue = new Queue<TStatus>();
        static void TreatRecv(TStatus s)
        {
            lock (sqLock)
            {
                statusQueue.Enqueue(s);
            }
            dequeWaiter.Set();
        }

        static void dequeAndRegister()
        {
            while (true)
            {
                dequeWaiter.Reset();
                if (statusQueue.Count == 0)
                {
                    dequeWaiter.WaitOne(5000);
                }
                else
                {
                    Stack<TStatus> deqpolling = new Stack<TStatus>(statusQueue.Count);
                    int c = 0;
                    lock (sqLock)
                    {
                        while (statusQueue.Count > 0 && c < 200)
                        {
                            deqpolling.Push(statusQueue.Dequeue());
                            c++;
                        }
                    }
                    if (c >= 10)
                    {
                        var act = new Action<IEnumerable<TStatus>>((s) =>
                            AutoReceiver.RegistStatuses(Data.Account.OuterInjectionSetFnFilterNRecv(s)));
                        act.BeginInvoke(deqpolling,
                            (iar) => ((Action<IEnumerable<TStatus>>)iar.AsyncState).EndInvoke(iar),
                            act);
                    }
                    else
                    {
                        AutoReceiver.RegistStatuses(Data.Account.OuterInjectionSetFnFilterNRecv(deqpolling));
                    }
                    System.Threading.Thread.Sleep(0);
                }
            }
        }
    
    }
}
